/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - core/containment
// Copyright( c) 2013.  All Rights Reserved
//
// File:		AEBoundingBox.h
// Author:		Gianluca Belardelli
// Date:		01/10/2013
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AEBOUNDINGBOX_H_
#define _AEBOUNDINGBOX_H_

/// Axis aligned bounding box.
class AE_DLLIMPEXP AEBoundingBox
{
// Members
private:
protected:
public:
	/// The minimum boundary of the AABB (i.e., the coordinates of the corner with the lowest numerical values).
	AEVector4f m_vcMin;

	/// The maximum boundary of the AABB (i.e., the coordinates of the corner with the highest numerical values).
	AEVector4f m_vcMax;
	
// Methods
private:
protected:
public:
	AE_DECLARE_NONVIRTUAL_CLASS_ALLOCATOR( TOBEDEFINED, AEBoundingBox );

	/** @name Costruttori */
	/// @{
	/// \brief
	/// Costruttore senza nessuna inizializzazione
	AE_FORCEINLINE AEBoundingBox( void );

	/// \brief Crea un nuovo AABB con le dimensioni specificate
	/// \param vcMin Specifica la coordinata del punto minimo del bounding box.
	/// \param vcMax Specifica la coordinata del punto massimo del bounding box.
	AE_FORCEINLINE AEBoundingBox( const AEVector4f &vcMin, const AEVector4f &vcMax );

	/// \brief Crea un nuovo AABB dai valori passati come parametri.
	/// \param fMinX Specifica la coordinata X del punto minimo del bounding box.
	/// \param fMinY Specifica la coordinata Y del punto minimo del bounding box.
	/// \param fMinZ Specifica la coordinata Z del punto minimo del bounding box.
	/// \param fMaxX Specifica la coordinata X del punto massimo del bounding box.
	/// \param fMaxY Specifica la coordinata Y del punto massimo del bounding box.
	/// \param fMaxZ Specifica la coordinata Z del punto massimo del bounding box.
	AE_FORCEINLINE AEBoundingBox( AEFLOAT32 fMinX, AEFLOAT32 fMinY, AEFLOAT32 fMinZ, AEFLOAT32 fMaxX, AEFLOAT32 fMaxY, AEFLOAT32 fMaxZ );

	/// \brief Crea un nuovo AABB copiando i dati dal bounding box passato come parametro.
	/// \param copy Il bounding box da copiare.
	AE_FORCEINLINE AEBoundingBox( const AEBoundingBox &copy );

	/// @}
	/** @name Funzioni statiche */
	/// @{
	/// \brief Ritorna un oggetto bounding box vuoto.
    static const AEBoundingBox &CreateEmptyAABB( void );

	/// @}
	/** @name Inizializzazione */
	/// @{
	/// \brief Imposta i valori di min e max tramite center e radius.
	/// \param vcCenter
	/// \param fRadius
	AE_FORCEINLINE void SetFromCenterRadius( const AEVector4f &vcCenter, const AEFLOAT32 &fRadius );

	/// \brief Imposta i valori di min e max corrispondenti al punto.
	/// \param vcPoint
	AE_FORCEINLINE void SetFromPoint( const AEVector4f &vcPoint );

	/// \brief Imposta i valori di min e max corrispondenti alla linea.
	/// \param vcXPoint
	/// \param vcYPoint
	AE_FORCEINLINE void SetFromLine( const AEVector4f &vcXPoint, const AEVector4f &vcYPoint );

	/// \brief Imposta i valori di min e max dai valori passati come parametri.
	/// @param fMinX Specifica la coordinata X del punto minimo del bounding box.
	/// @param fMinY Specifica la coordinata Y del punto minimo del bounding box.
	/// @param fMinZ Specifica la coordinata Z del punto minimo del bounding box.
	/// @param fMaxX Specifica la coordinata X del punto massimo del bounding box.
	/// @param fMaxY Specifica la coordinata Y del punto massimo del bounding box.
	/// @param fMaxZ Specifica la coordinata Z del punto massimo del bounding box.
	AE_FORCEINLINE void SetFromValues( AEFLOAT32 fMinX, AEFLOAT32 fMinY, AEFLOAT32 fMinZ, AEFLOAT32 fMaxX, AEFLOAT32 fMaxY, AEFLOAT32 fMaxZ );

	/// \brief Imposta questo BoundingBox calcolando l'unione dei due BoundingBox.Sets the AABB to be the union of two others.
	/// \param bbBox1 Primo BoundingBox.
	/// \param bbBox2 Secondo BoundingBox.
	AE_FORCEINLINE void SetUnion( const AEBoundingBox &bbBox1, const AEBoundingBox &bbBox2 );

	/// \brief Sets the box to all zero.
	/// \note This is a valid bounding box with a volume of zero.
	AE_FORCEINLINE void SetZero( void );

	/// \brief Sets the box to be the bounding box of the given set of points.
	/// \param lpPoints Pointer to an array holding a set of points.
	/// \param uiNumPoints Number of points that should be used from the array.
	/// \param uiStride The stride (in bytes) between each point. Must be at least sizeof (AEVector3f) for a pure AEVector3f array.
	///   can be larger, if there is other data in between the points.
	void SetFromPoints( const AEVector4f *lpPoints, AEUINT32 uiNumPoints, AEUINT32 uiStride = sizeof( AEVector4f ) );

	/// \brief Sets m_vcMin to the maximum positive AEFLOAT32 value, and m_vcMax to the maximum negative AEFLOAT32 value (min > max),
	/// making it an invalid box.
	/// The various expandToInclude functions can then be used to enlarge the box until it is large enough.
	/// \sa AEAlignedBoundingBox::ExpandToInclude
	AE_FORCEINLINE void SetInvalid( void );

	/// @}
	/** @name Intersezioni */
	/// @{
	/// \brief Returns true if the given AABB overlaps with this one. Zero volume overlaps are reported as an overlap.
	/// \param bbTestBox
	AE_FORCEINLINE AEBOOL32 Overlaps( const AEBoundingBox &bbTestBox ) const;

	/// \brief Returns true if the given object is inside this box or partially overlaps it.
	/// \param bsTestSphere
	AE_FORCEINLINE AEBOOL32 Overlaps( const AEBoundingSphere &bsTestSphere ) const;

	/// \brief Controlla se questo bounding box si interseca con quello specificato nel parametro.
	/// \param bbTestBox Il bounding box con il quale effettua il test di intersezione.
	/// \return AETRUE se il bounding box specificato si interseca con questo bounding box, altrimenti AEFALSE.
    AEBOOL32 Intersects( const AEBoundingBox &bbTestBox ) const;

	/// \brief Controlla se questo bounding box si interseca con la sfera specificata nel parametro.
	/// \param bsTestSphere La bounding sphere con la quale effettua il test di intersezione.
	/// \return AETRUE se la bounding sphere specificata si interseca con questo bounding box, altrimenti AEFALSE.
    AEBOOL32 Intersects( const AEBoundingSphere &bsTestSphere ) const;

	/// \brief Controlla se questo bounding box si interseca con la Frustum specificata nel parametro.
	/// \param frTestFrustum La frustum con la quale effettua il test di intersezione.
	/// \return AETRUE se la frustum specificata si interseca con questo bounding box, altrimenti AEFALSE.
    AE_FORCEINLINE AEBOOL32 Intersects( const AEFrustum &frTestFrustum ) const;

	/// \brief Controlla se questo bounding box si interseca con il piano specificato nel parametro.
	/// \param plTestPlane Il piano con il quale effettua il test di intersezione.
	/// \return AETRUE se il piano specificato si interseca con questo bounding box, altrimenti AEFALSE.
    AE_FORCEINLINE AEBOOL32 Intersects( const AEPlane &plTestPlane ) const;

	/// \brief Controlla se questo bounding box si interseca con il raggio specificato nel parametro.
	/// \param ryTestRay Il raggio con la quale effettua il test di intersezione.
	/// \return AETRUE se il raggio specificato si interseca con questo bounding box, altrimenti AEFALSE.
    AE_FORCEINLINE AEBOOL32 Intersects( const AERay &ryTestRay ) const;

	/// @}
	/** @name Controlli di validit&agrave; */
	/// @{

	/// \brief Is this a valid AABB? I.e. no NaNs and min[i] <= max[i].
	AE_FORCEINLINE AEBOOL32 IsValid( void ) const;

	/// \brief Returns true if the AABB is empty, e.g., its min is greater than its max for any dimension.
	AE_FORCEINLINE AEBOOL32 IsEmpty( void ) const;

	/// \brief Checks whether the two boxes are identical.
	/// \param bbRhs
	AE_FORCEINLINE AEBOOL32 IsIdentical( const AEBoundingBox &bbRhs ) const;

	/// \brief Checks whether the two boxes are equal withing some epsilon range.
	/// \param bbRhs
	/// \param fEpsilon
	AE_FORCEINLINE AEBOOL32 IsEqual( const AEBoundingBox &bbRhs, AEFLOAT32 fEpsilon ) const;

	/// \brief Checks whether the box is all zero within some epsilon range.
	/// \param fEpsilon
	AE_FORCEINLINE AEBOOL32 IsZero( AEFLOAT32 fEpsilon = AEMATH_DEFAULT_EPSILON ) const;

	/// \brief
	/// Calcola i vertici del triangle strip utilizzati per visualizzare il bounding box.
	//void ComputeVertices( AEVector3 vcVertex[8] ) const;

	/// @}
	/** @name Modifica */
	/// @{
	/// \brief
	/// Espande il bounding box per includere il punto dato.
	/// \param vcPoint
	AE_FORCEINLINE void IncludePoint( const AEVector4f &vcPoint );

	/// \brief
	/// Extends the AABB to include the given AABB.
	/// \param bbBox
	AE_FORCEINLINE void IncludeAabb( const AEBoundingBox &bbBox );

	/// \brief
	/// Extends the AABB to include the given AABB.
	/// \param bsSphere
	AE_FORCEINLINE void IncludeSphere( const AEBoundingSphere &bsSphere );

	/// \brief Increase the maximum and decrease the minimum in each direction by the given amount
	/// \param fValue
	AE_FORCEINLINE void ExpandBy( AEFLOAT32 fValue );

	/// \brief Makes all sides of the box as long as its longest side, thus making it a cube.
	AE_FORCEINLINE void ExpandToCube( void );

	/// \brief Reduces the box such that only the intersection with the given box remains.
	/// Box is invalid afterwards if the two boxes have no overlapping region.
	/// \param abbRhs Box to intersect with the callee.
	/// \sa AEAlignedBoundingBox::IsValid
	AE_FORCEINLINE void ReduceToIntersection( const AEBoundingBox &bbRhs );

	/// \brief Transforms the 8 corners in object-space and then recomputes the AABB.
	/// \param matTransform
	AE_FORCEINLINE void TransformFromCenter( const AEMatrix4f &matTransform );

	/// \brief Transforms the 8 corners in world-space and then recomputes the AABB.
	/// \param matTransform
	AE_FORCEINLINE void TransformFromOrigin( const AEMatrix4f &matTransform );

	/// \brief Translates the box in world-space.
	/// \param vcTranslation
	AE_FORCEINLINE void Translate( const AEVector4f &vcTranslation );

	/// \brief Scales the box's size, but does not change its position.
	/// \param vcScaleXYZ
	AE_FORCEINLINE void ScaleFromCenter( const AEVector4f &vcScaleXYZ );

	/// \brief Scales the box's min and max extents, thus changing its size and position.
	/// \param vcScaleXYZ
	AE_FORCEINLINE void ScaleFromOrigin( const AEVector4f &vcScaleXYZ );

	/// @}
	/// @name Accesso ai dati
	/// @{
	/// \brief Calcola il punto centrale del bounding box.
	/// \return Il punto centrale del bounding box come un vettore di tipo AEVector4f.
	AE_FORCEINLINE const AEVector4f GetCenter( void ) const;

	/// \brief Returns the box's extent along the X axis.
	AE_FORCEINLINE AEFLOAT32 GetSizeX( void ) const;

	/// \brief Returns the box's extent along the Y axis.
	AE_FORCEINLINE AEFLOAT32 GetSizeY( void ) const;

	/// \brief Returns the box's extent along the Z axis.
	AE_FORCEINLINE AEFLOAT32 GetSizeZ( void ) const;

	/// \brief Writes the 8 corners of the box to the provided array.
	/// \param lpOutVertices The array to which the corners are written. Must be large enough to store 8 vectors.
	/// \sa AEAlignedBoundingBox::GetCorner
	AE_FORCEINLINE void GetCorners( AEVector4f *lpOutVertices ) const;

	/// \brief Returns one corner of the box.
	/// \param bMaxX
	///   If set to true the X coordinate of the returned corner will be from the max extent.
	///   Otherwise it will be from the min extent.
	/// \param bMaxY
	///   If set to true the Y coordinate of the returned corner will be from the max extent.
	///   Otherwise it will be from the min extent.
	/// \param bMaxZ
	///   If set to true the Z coordinate of the returned corner will be from the max extent.
	///   Otherwise it will be from the min extent.
	/// \sa AEAlignedBoundingBox::getCorners
	AE_FORCEINLINE AEVector4f GetCorner( AEBOOL32 bMaxX, AEBOOL32 bMaxY, AEBOOL32 bMaxZ ) const;

	/// \brief Checks which of the extents (X,Y,Z) is the longest. Returns that value and its index (0,1,2).
	/// \param out_iAxis Optional. If set the index of the longest extent (0, 1 or 2) will be stored here.
	/// \return The length of the longest extent.
	AE_FORCEINLINE AEFLOAT32 GetMaxExtent( AEUINT32 *lpOutAxis = AENULL ) const;

	/// \brief Checks which of the extents (X,Y,Z) is the shortest. Returns that value and its index (0,1,2).
	/// \param out_iAxis Optional. If set the index of the shortest extent (0, 1 or 2) will be stored here.
	/// \return The length of the shortest extent.
	AE_FORCEINLINE AEFLOAT32 GetMinExtent( AEUINT32 *lpOutAxis = AENULL ) const;

	/// \brief Returns a AEBoundingSphere that perfectly fits around this box.
	//AE_FORCEINLINE AEBoundingSphere GetBoundingSphere( void ) const;

	/// @}
	/** @name Controlli sulla distanza */
	/// @{
	/// \brief Returns the squared distance of the point v to this box.
	/// For points that are inside the box this function returns zero.
	/// \param vcPoint The point for which the distance is computed.
	AE_FORCEINLINE AEFLOAT32 GetDistanceToSquared( const AEVector4f &vcPoint ) const;

	/// \brief Returns the squared distance of the given box to this box.
	/// If the two boxes overlap, zero is returned.
	/// \param abbRhs The box for which the distance is computed.
	AE_FORCEINLINE AEFLOAT32 GetDistanceToSquared( const AEBoundingBox &bbRhs ) const;

	/// \brief Returns the distance of the point v to this box.
	/// For points that are inside the box this function returns zero.
	/// \param vcPoint The point for which the distance is computed.
	AE_FORCEINLINE AEFLOAT32 GetDistanceTo( const AEVector4f &vcPoint ) const;

	/// \brief Returns the distance of the given box to this box.
	/// If the two boxes overlap, zero is returned.
	/// \param abbRhs The box for which the distance is computed.
	AE_FORCEINLINE AEFLOAT32 GetDistanceTo( const AEBoundingBox &bbRhs ) const;

	/// \brief Returns the distance of the given sphere to this box.
	/// If the two objects overlap, zero is returned.
	/// \param bsRhs The sphere for which the distance is computed.
	AE_FORCEINLINE AEFLOAT32 GetDistanceTo( const AEBoundingSphere &bsRhs ) const;
};

/// \brief Checks whether the two boxes are identical.
AE_FORCEINLINE AEBOOL32 operator== ( const AEBoundingBox &bbLhs, const AEBoundingBox &bbRhs );

/// \brief Checks whether the two boxes are not identical.
AE_FORCEINLINE AEBOOL32 operator!= ( const AEBoundingBox &bbLhs, const AEBoundingBox &bbRhs );

#include "AEBoundingBox.inl"

#endif // _AEBOUNDINGBOX_H_
